#ifndef CONTROLLER_H
#define CONTROLLER_H
#include <map>
#include "Direction.h"
#include <string>
#include "../Exceptions.h"
#include "../Model/Enums.h"
#include "GamePrototype.h"
#include "../Model/Game.h"
#include <vector>
#include "../View/Viewer.h"
#include "enums.h"
#include  "../View/tileInfo.h"

using namespace std;
namespace View{
	class PlayerControl;
	class Admin;
}

namespace Control{
	class Controller
	{
	private:

		// a pointer to the model managed by this controller
		Model::Game* myGame;
		//a pointer to the existing Controller instance, if it exists
		static Controller* mySelf;
		//the status of the game
		GameStatus gs;

		// the step in the current turn
		TurnStep ts;

		//the map registering all player and their control
		std::map<int,View::PlayerControl*> playerAssociation;
		//a vector registering all viewers of the game
		std::vector<View::Viewer*> viewerList;
		//the currentPlayer
		std::map<int,View::PlayerControl*>::iterator CurrentPlayer;

		


		/**
		Check if the given playerControl can manage this player
		@param idPlayer the id of the player
		@param pc a pointer to the requiring playerControl class
		@return true if it matches the stored data, else false;
		*/
		bool checkAccess(int player, View::PlayerControl *pc);

		/**
		check if this player is allowed to play now
		@param idPlayer the id of the player to be checked
		@return true if this player is allowed to play , else false
		*/
		bool canPlay(int player);

		/**
		check if the given player can play and if the given PlayerControl is allowed to alter the given player		
		@param idPlayer the id of the player
		@param pc a pointer to the requiring playerControl class
		@return true if all test are successfull , else false 
		*/
		bool checkAccessAndPlayer(int player, View::PlayerControl* pc);

		/**
		Notifiy all the viewers that a change occured in the model
		*/
		void notifyAllMapChanged();

		/**
		notifies all the viewers that a changed occured in the players statistics
		*/
		void notifyAllPlayerChanged();

	public:

		/**
		getter
		@returns informations about the free tile
		*/
		View::TileInfo getFreeTileInfo();

		/** 
		move the player
		@param idPlayer the id of the player requesting the action
		@param pc the id of the player control requestion the action
		*/
		void move(int idPlayer,View::PlayerControl* pc,Direction dir);

		/**
		method to get a valid pointer to a Controller Class
		(required for the singleton Design patern)
		@return a pointet to a valid Controller class
		*/
		static Controller* getInstance(); 
		/**
		Register a new Viewer to the controller
		@param pc a pointer to a PlayerControl
		@return the id of the player
		*/
		void reg(View::Viewer* pc)throw (IllegalOperation);

		/**
		unregister a viewer for a player
		@param idPlayer the id of the player to be unregistered
		@param pc a pointer to the View to be unregistered
		@throw IllegalOperation if the operation cannot be done at the moment : no such player or game started
		*/
		void unreg(View::Viewer* pc);
		//		bool deplaceColor(Direction dir);
		/**
		Rotate the free tile
		@param idPlayer the id of the player requesting the action
		@param pc the id of the player control requestion the action
		@param dir the direction in which to rotate the tile
		*/
		void rotateTile(int player, View::PlayerControl *pc,Model::Orientation dir);

		/**
		Rotate the free tile one step to the right
		@param idPlayer the id of the player requesting the action
		@param pc the id of the player control requestion the action

		*/
		void rotateTile(int player, View::PlayerControl *pc);

		/**
		Place a tile on the board by pushing others
		@param idPlayer the id of the player requesting the action
		@param pc the id of the player control requestion the action
		@param row the row /column in which to push
		@param dir the direction (S->N E->W and so)
		*/
		void placeTile(int player, View::PlayerControl *pc,int id); // the 'row' can be a column and the dir is back or front

		//void chooseColor(string player, string colorName);
		//void chgCurrentPlayer(int newCurrentPlayer);

		/**
		getter
		@return a map containing pairs composed of idPlayer/name of the Player
		*/
		map<int,string> getPlayersName();


		/**
		getter
		@param idPlayer the id of the player requesting the action
		@param pc the id of the player control requestion the action
		@return the next treasure of the given player
		*/
		string getPlayerNextTreasure(int idPlayer,View::PlayerControl* pc);

		/**
		getter
		@return the id of the current Player
		*/
		int getCurrentPlayer();

		/**
		getter
		@return the status of the game
		*/
		GameStatus getCurrentStatus();

		/**
		notify the Controller that this player has ended moving
		@param idPlayer the id of the player requesting the action
		@param pc the id of the player control requestion the action
		@return true if all actions have been done, else false
		*/
		bool endTurn(int idPlayer,View::PlayerControl* pc);

		friend class View::Admin;

		/**
		getter
		@return a TileInfo Object containing everything needed by a view to display a tile
		*/
		View::TileInfo getTileInfo(int row,int column);



	private:

		void notifyAllStateChanged();


		/**
		remove a player
		@param idPlayer the id of Player to remove
		*/
		void removePlayer(int idPlayer);
		/**
		add a player
		@param name the name of the new player
		@param pc the player control which will control it
		*/
		void addPlayer(string name,View::PlayerControl* pc);

		/**
		Run the finals initialisations, start the game and notifies all the viewers
		*/
		void startGame();
		/** 
		This constructors are private and thus shouldn't be called outside this Class!
		*/
		Controller(void);
		~Controller(void);
	};}
#else
namespace Control{
	class Controller;
}
#endif
